<?php
	/**
	 ** .: MyDebuger v0.2 :.
	 **
	 ** Version:
	 **	v0.2
	 ** Fecha Actualización:
	 **	2009-11-21
	 ** Descripcion:
	 **	Clase abstracta para manejar depuracion y errores
	 ** Autor:
	 **	Hermann D. Schimpf
	 ** Contacto
	 **	hschimpf@gschimpf.com
	 ** 
	 **/
	abstract class MyDebuger {
		/**
			public function _defaultMethodFormat_() {
				try {
					try {
						// CODE HERE
					} catch (ErrorException $e) {
						// capturamos todos los errores y los transformamos en mi excepcion
						$this->convertToMyException($e);
					}
				} catch (MyException $e) {
					// si se produjo una excepcion la manejamos con el metodo 'show'
					$this->show($e->getMensaje($this->html));
				}
			}
		 **/
			
		private $version				= 'v0.2';
		private $author				= 'Hermann D. Schimpf';
		private $authorAddress		= 'hschimpf@gschimpf.com';
		/* nombre de la instancia */
		private $name					= False;
		/* atributo con el ultimo error producido */
		private $lastError			= False;
		/* fichero de log para el registro de depuracion */
		private $logFile				= Null;
		/* bandera para mostrar el tiempo en los mensajes */
		private $saltoMsg				= False;
		/* bandera para des / habilitar la salida de depuracion */
		private $debugStat			= False;
		/* bandera para des / habilitar la salida de depuracion en formato HTML */
		protected $html					= False;
		/* cadena para mensajes */
		private $cadenaShow			= '';
		/* atributo de tiempo de ejecucion actual */
		private static $timeNow		= 0;
		/* atributo de tiempo de ejecucion inicial */
		private static $timeStart	= 0;
		/* bandera para des / habilitar el tiempo de espera para la depuracion */
		private $timeEnabled			= False;
		/* velocidad del tiempo para la depuracion */
		private $timeStep				= 500000;
		
		public function __construct($nombre = __CLASS__) {
			/* verificamos si existe la clase necesaria para el funcionamiento */
			$this->verificarClase();
			/* asignamos el manejador de errores al metodo interno */
			set_error_handler(__CLASS__ . '::manejadorErrores', E_ALL);
			/* almacenamos el tiempo de inicio */
			$this->saveTime();
			/* asignamos el nombre a la instancia */
			$this->name = $nombre;
		}
		
		public function __destruct() {
			/* verificamos si el archivo fichero log existe */
			if ($this->logFile !== Null) {
				/* finalizamos el fichero log agregando un espacio vacio */
				$this->saveLog('', True, False);
				/* cerramos el fichero log */
				fclose($this->logFile);
			}
			/* restauramos el manejador de errores */
			restore_error_handler();
		}
		
		public final function enableDebug($enable = True) {
			/* habilitamos la salida */
			$this->debugStat = $enable;
			/* mostramos un mensaje */
			$this->show('Habilitando depuracion..', False);
			/* mostramos ok al mensaje */
			$this->showOK();
		}
		
		public final function enableHtmlDebug() {
			/* habilitamos el formateo HTML */
			$this->html = True;
			/* habilitamos la salida de mensajes */
			$this->enableDebug();
		}
		
		public final function enableDemoTime($fast = False) {
			/* habilitamos la bandera de demo para el tiempo */
			$this->timeEnabled = True;
			/* verificamo si se desea mas rapido */
			if ($fast === True)
				/* aceleramos el tiempo */
				$this->timeStep = 100000;
		}
		
		public final function getError($vaciar = False, $interno = False) {
			try {
				try {
					/* verificamos si es para utilizacion interna */
					if ($interno === True)
						/* retornamos aqui el error para no mostrar mensajes */
						return $this->lastError;
					else
						/* almacenamos el error porque puede eliminarse mediante la bandera 'vaciar' */
						$errorReturn = $this->lastError;
					/* mostramos un mensaje */
					$this->show('Enviando ultimo error..', False);
					/* verificamos si se especifico vaciar */
					if ($vaciar === True)
						/* eliminamos el ultimo error */
						$this->setError();
					/* mostramos OK al mensaje */
					$this->showOK();
					/* retornamos el error */
					return $errorReturn;
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		public final function enableLog($fileName = Null) {
			try {
				try {
					/* verificamos si especifico un nombre para el fichero log */
					if ($fileName === Null)
						/* salimos con una excepcion */
						$this->newException('No se especifico el nombre para el fichero log');
					/* verificamos si ya existe un fichero log abierto */
					if ($this->logFile !== Null)
						/* salimos con una excepcion */
						$this->newException('Ya existe un fichero log abierto');
					/* habilitamos el log */
					$this->crearLogFile($fileName);
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		public static function manejadorErrores($numero, $mensaje, $archivo, $linea) {
			/* modificamos los errores de ejecucion a excepciones */
			throw new ErrorException($mensaje, 0, $numero, $archivo, $linea);
		}
		
		public final function testDebuger() {
			try {
				try {
					/* iniciamos las pruebas */
					$this->startTest();
					/* mostramos unos mensajes */
					$this->showMensajesTest();
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		private function startTest() {
			/* habilitamos el tiempo demo */
			$this->enableDemoTime(True);
			/* habilitamos la depuracion */
			$this->enableHtmlDebug();
			/* abrimos un fichero log */
			$this->enableLog('testDebuger.log');
		}
		
		private function showMensajesTest() {
			$this->show('Nombre de la clase:', False);
			$this->show(__CLASS__);
			$this->show('Version:', False);
			$this->show($this->version);
			$this->show('Autor:', False);
			$this->show($this->author);
			$this->show('Contacto:', False);
			$this->show($this->authorAddress);
			$this->show('Mensaje de prueba sin salto..', False);
			$this->show('Respuesta al mensaje anterior (con salto..)');
			$this->show('Probando el comando ok..', False);
			$this->showOK();
			$this->show('Mensaje en espera de respuesta..', False);
			$this->show($this->rojo('existe el metodo ' . $this->italic('rojo') . '..?'), False);
			$this->showOK();
			$this->show('Perfecto..!');
			$this->show('Ahora saltaremos una excepcion..', False);
			try{try{$this->show($estoNoExiste);}catch(ErrorException $e){$this->convertToMyException($e);}}catch(MyException $e){$this->show($e->getMensaje($this->html));}
			$this->show('La excepcion funciono correctamente!');
		}
		
		private function verificarClase($clase = 'MyException') {
			/* verificamos si existe */
			if (!class_exists($clase))
				/* si no existe matamos el proceso con un mensaje */
				die('Se necesita de la clase ' . $this->italic($clase) . ' para utilizar la clase ' . $this->italic(__CLASS__));
		}
		
		protected final function getTime() {
			/* obtenemos el tiempo actual */
			$timeInicio = explode(' ', microtime());
			/* retornamos la suma del tiempo actual */
			return $timeInicio[1] + $timeInicio[0];
		}
		
		protected final function setError($error = False) {
			/* guardamos el error */
			$this->lastError = $error;
		}
		
		protected final function show($cadena = Null, $salto = True) {
			try {
				try {
					/* verificamos si especifico una cadena para mostrar */
					if ($cadena === Null)
						/* salimos con una excepcion */
						$this->newException('No se especifico la cadena a mostrar');
					/* esperamos un tiempo aleatorio */
					$this->sleepRand();
					/* almacenamos la cadena */
					$this->cadenaShow = $cadena;
					/* armamos la cadena de salida y la mostramos */
					$this->armarMostrarCadena($salto);
					/* almacenamos la cadena en el log */
					$this->saveLog($cadena, $salto);
					/* modificamos la bandera */
					$this->saltoMsg = True;
					/* verificamos si es un salto */
					if ($salto === True)
						/* modificamos la bandera */
						$this->saltoMsg = False;
					/* vaciamos el buffer de salida */
					$this->vaciarBuffer();
					/* esperamos un tiempo aleatorio */
					$this->sleepRand();
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		protected function showOK() {
			/* mostramos la cadena OK */
			$this->show('OK', True);
		}
		
		protected final function italic($cadena = Null) {
			try {
				try {
					/* verificamos si especifico la cadena */
					if ($cadena === Null)
						/* salimos con una excepcion */
						$this->newException('No se especifico la cadena a formatear');
					/* verificamos si esta habilitada la bandera de HTML */
					if ($this->html === True)
						/* formateamos la cadena */
						$cadena = "<i>$cadena</i>";
					/* retornamos la cadena formateada */
					return $cadena;
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		protected final function rojo($cadena = Null) {
			try {
				try {
					/* verificamos si especifico la cadena */
					if ($cadena === Null)
						/* salimos con una excepcion */
						$this->newException('No se especifico la cadena a formatear');
					/* verificamos si esta habilitada la bandera de HTML */
					if ($this->html === True)
						/* formateamos la cadena */
						$cadena = "<font style='color:#a00;'>$cadena</font>";
					/* retornamos la cadena formateada */
					return $cadena;
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		protected final function getTimeNow() {
			/* agregamos el tiempo transcurrido al tiempo actual */
			self::$timeNow = $this->getTime() - self::$timeStart;
			/* retornamos el tiempo con formato de 4 decimales y 7 digitos */
			return str_pad(number_format(self::$timeNow, 3, ',', '.'), 7, '0', STR_PAD_LEFT);
		}
		
		protected final function newException($msg = False) {
			/* creamos una exepcion temporal para obtener el lugar de origen y agregarlo al error */
			$tempException = new MyException('error');
			/* asignamos el error con el lugar de origen (llamada al metodo) */
			$this->setError($msg . $tempException->getFrom($this->html));
			/* enviamos una exepcion para mostrar el error */
			throw new MyException($this->getError(False, True));
		}
		
		protected function convertToMyException($exception) {
			/* obtenemos el mensaje de error */
			$msg = $exception->getMessage();
			/* obtenemos el fichero de origen */
			$file = basename($exception->getFile());
			/* verificamos si la cadena de error ya tiene el nombre del fichero */
			if (!strstr($msg, $file))
				/* si no tiene agregamos el nombre del fichero y la linea de error */
				$msg	.= ', called in ' . $file . ':' . $exception->getLine();
			/* enviamos la nueva excepcion */
			$this->newException($msg);
		}
		
		private function getDebugStatus() {
			/* retornamos el estado de la depuracion */
			return $this->debugStat;
		}
		
		private function getName() {
			/* retornamos el nombre actual */
			return $this->name;
		}
		
		private final function saveTime() {
			/* guardamos el tiempo de inicio */
			self::$timeStart = $this->getTime();
		}
		
		private function vaciarBuffer() {
			/* vaciamos el buffer de salida */
			flush();
		}
		
		private function sleepRand() {
			/* verificamos si esta habilitada la bandera de tiempo */
			if ($this->timeEnabled)
				/* esperamos un tiempo aleatorio */
				usleep(rand(1000, $this->timeStep));
		}
		
		private function armarMostrarCadena($salto) {
			/* agregamos el titulo y tiempo a la cadena */
			$this->agregarTituloTiempo();
			/* formateamos la cadena con HTML */
			$this->formatearCadena($salto);
			/* verificamos si esta habilitada la bandera de depuracion */
			if ($this->getDebugStatus() === True)
				/* mostramos la cadena de salida */
				echo $this->cadenaShow;
		}
		
		private function agregarTituloTiempo() {
			/* almacenamos la cadena con el titulo y el tiempo actual */
			$this->cadenaShow = (
				/* verificamos si mostramos el nombre */
				$this->saltoMsg == False ?
				/* agregamos el tiempo y nombre de la clase */
				'[' . $this->getTimeNow() . ' ' . $this->getName() . '::debug] ' :
				/* no agregamos nada */
				''
			/* agregamos la cadena con los HTML */
			) . $this->cleanHTML($this->cadenaShow);
		}
		
		private function formatearCadena($salto) {
			/* verificamos si esta habilitada la bandera de HTML */
			if ($this->html === True)
				/* formateamos la cadena dentro de la etiquera CODE */
				$this->cadenaShow = "<code>{$this->cadenaShow}</code>";
			/* agregamos el salto de linea a la cadena */
			$this->agregarSaltoLinea($salto);
		}
		
		private function agregarSaltoLinea($salto) {
			/* verificamos si esta habilitada la bandera de salto de linea */
			if ($salto === True)
				/* agregamos el salto a la cadena */
				$this->cadenaShow .= (
					/* verificamos si esta habilitada la bandera de HTML */
					$this->html === True ?
					/* agregamos el salto en formato HTML */
					"<br/>\n" :
					/* agregamos el salto sin formato */
					"\n"
				);
			/* si no, agregamos un espacio */
			else
				$this->cadenaShow .= ' ';
		}
		
		private function saveLog($msg, $salto, $showName = True) {
			try {
				try {
					/* verificamos si esta habilitado el log */
					if ($this->logFile !== Null) {
						/* almacenamos el mensaje en el fichero log */
						fwrite($this->logFile, (
							/* verificamos si mostramos la cadena con el nombre */
							$showName === True && $this->saltoMsg === False ?
							/* agregamos el tiempo y nombre de la clase */
							'[' . $this->getTimeNow() . ' ' . $this->getName() . '::log] ' :
							/* no agregamos nada */
							''
						/* agregamos el mensaje sin las etiquetas html */
						) . strip_tags($msg) . (
							/* verificamos si agregamos un salto */
							$salto === True ?
							/* agregamos el salto */
							"\n" :
							/* agregamos un espacio */
							' '
						));
					}
				} catch (ErrorException $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		private function crearLogFile($fileName) {
			/* mostramos un mensaje */
			$this->show('Habilitando log en el fichero ' . $this->italic($fileName) . '..', False);
			/* habilitamos el fichero para almacenar el log */
			$this->logFile = fopen($fileName, 'a+');
			/* iniciamos el fichero log */
			$this->saveLog('----- ' . $this->getName() . '::log ' . date('Y-m-d H:i:s') . ' -----', True, False);
			/* habilitamos la bandera para que muestre el nombre */
			$this->saltoMsg = False;
			/* almacenamos el mensaje mostrado */
			$this->saveLog('Habilitando log en el fichero ' . $this->italic($fileName) . '..', False);
			/* deshabilitamos la bandera */
			$this->saltoMsg = True;
			/* mostramos ok al mensaje */
			$this->showOK();
		}
		
		private function cleanHTML($cadena) {
			/* reemplazamos todas las etiquetas HTML */
			$cadena = str_replace(
				Array('<i>', '</i>', '<font', '</font>', '\'>'),
				Array('|i|', '|/i|', '|font', '|/font|', '\'|'),
				$cadena
			);
			/* reemplazamos los signos mayor y menor por su codigo */
			$cadena = str_replace(
				Array('<', '>'),
				Array('&lt;', '&gt;'),
				$cadena
			);
			/* retornamos la cadena formateada */
			return str_replace(
				Array('|i|', '|/i|', '|font', '|/font|', '\'|'),
				Array('<i>', '</i>', '<font', '</font>', '\'>'),
				$cadena
			);
		}
	}
?>
